#!/usr/bin/env python

"""
NetSpy Client		
by Marcin Ciechowicz for ZPR
v0.01
"""

import socket
import json
from subprocess import call
import logging
import argparse
import os.path

appName = 'NetSpyClient'
scriptDir = 'scripts'

logger = logging.getLogger(appName)

def initLogger():
	formatter = logging.Formatter('%(asctime)s %(levelname)s::%(message)s')
	hdlrFile = logging.FileHandler(appName + '.log')
	hdlrFile.setFormatter(formatter)
	hdlrStd = logging.StreamHandler()
	hdlrStd.setFormatter(formatter)
	logger.addHandler(hdlrFile)
	logger.addHandler(hdlrStd) 
	logger.setLevel(logging.DEBUG)

class Service:
	
	name = ''
	testerPath = ''

	def __init__(self, name, testerPath):
		"""Creates service"""
		self.name = name
		self.testerPath = testerPath

	def getName(self): 
		return self.name
	def getCheckerPath(self): 
		return self.checkerPath

	def executeCheck(self):
		return call(testerPath,'1')

	
		

class Config:

	options = {}

	def __init__(self, fileName = "netspy.conf"):
		"""Initialize config from file """
		self.options = {'port' : '', 'server_port' : '', 'server_ip': '', 'service_list': [] }
		self.fileName = fileName
		self.loadFromFile(self.fileName)
	def loadFromFile(self,fileName):
		try:
			logger.info("Reading config file: " + fileName)
			configFile = file(fileName,'r')
			line = configFile.readline()
			active_options = ('port','server_port','server_ip')
			while line != '' :
				tokens = line.strip().split(' ')
				if tokens[0] == 'service':
					if (tokens[1] == 'alive'):
						logger.warning("Service " + tokens[1] + " is already definied, please use different name")
					elif (os.path.isfile(scriptDir+'/'+tokens[2])):	
						self.options['service_list'].append(Service(tokens[1],tokens[2]))
						logger.debug("New service added: " + tokens[1])
					else:
						logger.warning("Service " + tokens[1] +" error: Can't find script : " + scriptDir + '/' + tokens[2])
						logger.warning("Service " + tokens[1] +" creation failed")
				elif tokens[0] in active_options:
					self.options[tokens[0]]=tokens[1]	
					logger.debug(tokens[0] + " set to " + tokens[1])
				else:
					logger.warning("Unkown option " + tokens[0])
				line = configFile.readline()
			configFile.close()
		except IOError:
			logger.error( "Can't read " + fileName)
			exit()	

	def getPort(self): 
		return self.options['port']
	def getServerPort(self): 
		return self.options['server_port']
	def getServerIp(self): 
		return self.options['server_ip']
	def getServiceList(self):
		return self.options['service_list']


class ClientApp:

	config = ''
	def getHash(self):
		hashValue=''
		try:
			hashFile=file(".netspy.hash","r")
			hashValue = hashFile.readline()
		except IOError:
			logger.warining( "No hash found")
		finally:
			return hashValue
			
	def setHash(self,hashValue):
		""" Function doc """
		if (hashValue!=''):
			try:
				hashFile=file(".netspy.hash","w")
				hashFile.write(hashValue)
			except IOError:
				logger.error( "Can't store hash value")
				exit(0)
					
	def setConfig(self, config):
		self.config = config

	def __init__(self,config=None):
		if config!=None:
			self.setConfig(config)
	
	def hashCheck(self,hashValue):
		return True
	
	def init(self):
		logger.info('Client - running ')
	
	def registerAtServer(self):
		try:
			server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			server.connect((self.config.getServerIp(), int(self.config.getServerPort()) ))
		except IOError:
			logger.error("Can't register at monitoring server - connection problem")
			return False

		service_list = [];
		for i in self.config.getServiceList():
			service_list.append(i.getName())

		service_list.append('alive')

		message = { 'command' : 'register', 'payload': {'hash' : self.getHash(), 'port' : self.config.getPort(), 'service_list' : service_list }}
		messageToSend = json.dumps(message)
		server.send(messageToSend)
		data = server.recv(1024)
		server.close()
		answer = json.loads(data)
		
		if (answer['command'] != 'register'): 
			logger.error("Bad command type - expected 'register'")
			return False
		if (answer['payload']['status'] == 0):
			logger.info("Reusing old hash") 
			return True
		elif (answer['payload']['status'] == 1):
			logger.info("Saving new hash: " + str(answer['payload']['hash']))
			hashValue = answer['payload']['hash']
			self.setHash(str(hashValue))
			return True
		elif (answer['payload']['status'] == 2):
			clear = file('.netspy.hash','w')
			clear.write('')
			return False
		else:
			return False
	
	def performServiceCheck(self,message):
		try:
			question = json.loads(message)
			if question['command'] != 'check_status':
				logger.error("Unknown command '" + question['command'] + "'received from server")
				logger.error("No check performed")
				return
			else:
				logger.info("Performing check")
				resultList = []
				alive = { 'alive' : 0 }
				resultList.append(alive)
				for service in self.config.getServiceList():
					tmp = service.executeCheck()
					result = {service.getName() : tmp }
					resultList.append(result)
				answer = {'command' : 'check_status', 'payload' : {'check_result' : resultList }}
				return json.dumps(answer);
		
		except ValueError:
			logger.error("Unsupported command format")
			logger.error("No check performed")
		
	def run(self):
		logger.info("Client - registering at monitoring server")
		i=0
		while (i<3 and not self.registerAtServer()):
			i=i+1

		if (i==3):
			logger.error("Connect to monitoring server failed - can't connect to specified host")
			logger.error("Please check your config file")
			return 

		logger.info("Client - register succesful")
		client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		client.bind(('',int(self.config.getPort())))	
		client.listen(5)
		logger.info('Client - waiting for commands from server')
		
		while 1:
			request, address = client.accept()
			message = request.recv(1024)
			answer = self.performServiceCheck(message)
			request.send(answer)
			request.close()
		

def parseArgs():

	parser = argparse.ArgumentParser(prog=appName, usage='%(prog)s [options]')
	parser.add_argument('--verbose','-v', action='store_false', help='verbose mode')
	parser.add_argument('--config','-c', action='store', help='config filename')

	args = parser.parse_args()
	if args.verbose == True:
		logger.setLevel(logging.ERROR)
	if args.config != None:
		return Config(args.config)

	else:
		return Config()

#---------- main --------------------------#

initLogger()
client = ClientApp(parseArgs())
client.init()
client.run()

#-----------------------------------------#
